`include "common_header.verilog"

//  *************************************************************************
//  File : grm_fifo.v
//  ****************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited. 
//  Copyright (c) 2002-2003-2004 MoreThanIP
//  Designed by Daniel Koehler
//  fbalay@morethanip.com
//  ****************************************************************************
//  Description: Generic Rate Matching FIFO with reduced latency and jumbo frame setting.
//  Version    : $Id: xgrm_fifo.v,v 1.7 2008/01/30 16:01:13 dk Exp $
//  ****************************************************************************

module xgrm_fifo (

   reset_wr_clk,
   reset_rd_clk,
   jumbo_en,
   wr_clk,
  `ifdef USE_CLK_ENA
   wr_clk_ena,
  `endif
   rd_clk,
  `ifdef USE_CLK_ENA
   rd_clk_ena,
  `endif
   wr_fail,
   rd_fail,
   ln_data,
   ln_control,
   ln_ena,
   delete4_ok,
   out_dat,
   out_ctl);
   
parameter RF_ON_FAULT = 0;

input   reset_wr_clk;           //  Reset - wr_clk Domain
input   reset_rd_clk;           //  Reset - rd_clk Domain
input   jumbo_en;               //  increase thresholds(=latency) to support jumbo frames
input   wr_clk;                 //  161MHz FIFO Write Clock
input   rd_clk;                 //  156MHz Read Clock
output  wr_fail;                //  write error (Overflow)
output  rd_fail;                //  read error (Underflow)
input   [63:0] ln_data;         //  Data from PCS
input   [7:0] ln_control;       //  Control from PCS
input   ln_ena;                 //  Data / Control Write Enable
input   delete4_ok;             //  deletion of 31..0 of current word is o.k.
output  [63:0] out_dat;         //  outout data
output  [7:0] out_ctl;          //  outout control

`ifdef USE_CLK_ENA
input   wr_clk_ena;
input   rd_clk_ena;
`endif

wire   [63:0] ln_data_int;         //  Data from PCS
wire   [7:0]  ln_control_int;       //  Control from PCS

wire    wr_fail; 
reg     rd_fail; 
reg     [63:0] out_dat; 

reg     [7:0] out_ctl; 
reg     mdel4; 
reg     deleting;       //  delete is progressing currently
reg     [31:0] ln_datu; //  saved upper column data
reg     [3:0] ln_ctlu;  //  saved upper column ctl
reg     [71:0] lb_din;  //  Input Data / Control
//reg     lb_wren;        //  FIFO Write Enable
wire    lb_wren_c; //  FIFO Write Enable, combinatorial
wire    [71:0] ff_din;  //  Input Data / Control
wire    ff_wren;        //  FIFO Write Enable
wire    ff_rden;        //  FIFO Read Enable
reg     [71:0] ff_dout; //  Input Data / Control

//  FIFO Status
//  -----------

wire    ff_sav;         //  Section Available
wire    ff_aempty;      //  FIFO Almost Empty
wire    ff_septy;       //  Section Empty Available
wire    ff_afull;       //  FIFO Almost Full
reg     septy_l;        //  septy latched for 
reg     ff_afull_d;     //  FIFO status delayed


//  XGMII Output Mux Control
//  ------------------------

wire    [71:0] ff_dout_int; 
reg     ff_insert;

// read STM
// --------

parameter RSTM_TYP_FAULT     = 2'b 00;
parameter RSTM_TYP_READ      = 2'b 01;
parameter RSTM_TYP_INSERT    = 2'b 10;

reg     [1:0] state;
reg     [1:0] nextstate;

//write side threshold 
reg     wjumbo_en_reg1;
reg     wjumbo_en_reg2;         
reg     [4:0] sep_section; 

//read side threshold
reg     rjumbo_en_reg1;
reg     rjumbo_en_reg2;  
reg     [4:0] sav_section; 

// select write side threshold
// ---------------------------
always @(posedge reset_wr_clk or posedge wr_clk)
begin
        if (reset_wr_clk == 1'b 1)
        begin
                wjumbo_en_reg1 <= 1'b 0;
                wjumbo_en_reg2 <= 1'b 0;
                sep_section    <= 5'b 00111;
        end
        else
        begin
                wjumbo_en_reg1 <= jumbo_en;
                wjumbo_en_reg2 <= wjumbo_en_reg1;
        
                if( wjumbo_en_reg2==1'b 1 )
                begin
                        sep_section <= 5'b 01000;
                end
                else
                begin
                        sep_section <= 5'b 00111;
                end
        end
end
        
// select read side threshold
// ---------------------------
always @(posedge reset_rd_clk or posedge rd_clk)
begin
        if (reset_rd_clk == 1'b 1)
        begin
                rjumbo_en_reg1 <= 1'b 0;
                rjumbo_en_reg2 <= 1'b 0;
                sav_section    <= 5'b 00011;
        end
        else
        begin
                rjumbo_en_reg1 <= jumbo_en;
                rjumbo_en_reg2 <= rjumbo_en_reg1;
        
                if( rjumbo_en_reg2==1'b 1 )
                begin
                        sav_section <= 5'b 00100;
                end
                else
                begin
                        sav_section <= 5'b 00011;
                end
        end
end

//  Insert LOCAL FAULT when FIFO overrun occurs
//  ------------------------------
always @(posedge reset_wr_clk or posedge wr_clk)
   begin : povrlf
   if (reset_wr_clk == 1'b 1)
      begin
      ff_afull_d    <= 1'b 0;
      end
   else
        begin
         `ifdef USE_CLK_ENA
            if(wr_clk_ena == 1'b 1)
            begin
         `endif

            ff_afull_d <= ff_afull;

         `ifdef USE_CLK_ENA
            end
         `endif       

        end
end

assign ln_data_int      = (ff_afull_d==1'b 1) ? 64'h 0100009c0100009c   : ln_data;
assign ln_control_int   = (ff_afull_d==1'b 1) ? 8'h 11                  : ln_control;


//  Input Delete 31...0 Mux
//  ------------------------------

always @(posedge reset_wr_clk or posedge wr_clk)
   begin : process_1
   if (reset_wr_clk == 1'b 1)
      begin
      lb_din        <= {72{1'b 0}};	
      mdel4         <= 1'b 0;	
      deleting      <= 1'b 0;	
      septy_l       <= 1'b 1;	
      ln_datu       <= {32{1'b 0}};	
      ln_ctlu       <= {4{1'b 0}};	
      end
   else
      begin

         `ifdef USE_CLK_ENA
            if(wr_clk_ena == 1'b 1)
            begin
         `endif
      
                //  save upper column
              
              if (ln_ena == 1'b 1)
                 begin
                 ln_datu <= ln_data_int[63:32];	
                 ln_ctlu <= ln_control_int[7:4];	
                 end
        
                //  lower column switches with mdel4.
              
              if (mdel4 == 1'b 1)
                 begin
                 lb_din[67:64] <= ln_ctlu;	        //  saved upper in low column
                 lb_din[31:0] <= ln_datu;	
                 end
              else
                 begin
                 lb_din[67:64] <= ln_control_int[3:0];	//  1:1
                 lb_din[31:0]  <= ln_data_int[31:0];	
                 end
                //  upper column switches back 1 earlier due to the shift
        
              if (mdel4 == 1'b 1 & delete4_ok == 1'b 0)
                 begin
                 lb_din[71:68] <= ln_control_int[3:0];	//  current low column in upper 
                 lb_din[63:32] <= ln_data_int[31:0];	
                 end
              else
                 begin
                 lb_din[71:68] <= ln_control_int[7:4];	//  normal and just before changing back to normal
                 lb_din[63:32] <= ln_data_int[63:32];
                 end
        
                //  latch a section empty event indicating buffer full tendency
        
              if (ff_septy == 1'b 0 & mdel4 == 1'b 0 & deleting == 1'b 0)       //  set only when deletion is not already ongoing
                 begin
                 septy_l <= 1'b 0;	
                 end
              else if (mdel4 == 1'b 1 )
                 begin
                 septy_l <= 1'b 1;	//  clear when deletion is in progress
                 end
        
                //  control the mux, delete 4 if nearly full and deletion is o.k.
                
              if (ln_ena == 1'b 1 & septy_l == 1'b 0 & delete4_ok == 1'b 1 & mdel4 == 1'b 0)
                 begin
                 mdel4 <= 1'b 1;	//  delete another one as soon as possible
                 end
              else if (ln_ena == 1'b 1 & delete4_ok == 1'b 1 & mdel4 == 1'b 1 )
                 begin
                 mdel4 <= 1'b 0;	
                 end
        
                //  indicate that we deleted
        
              if (ln_ena == 1'b 1 & delete4_ok == 1'b 1 & mdel4 == 1'b 1)
                 begin
                 deleting <= 1'b 1;	//  mdel4 falling edge detected
                 end
              else
                 begin
                 deleting <= 1'b 0;	
                 end
        
         `ifdef USE_CLK_ENA
            end
         `endif       
      
      end
   end

//  write failure indication

assign wr_fail = ff_afull; 


//  FIFO Write Control
//  ------------------

//  when going from normal into mdel4 we pause 1 cycle as we need to wait for next word first

assign lb_wren_c = (ln_ena == 1'b 1 & septy_l == 1'b 0 & delete4_ok == 1'b 1 & mdel4 == 1'b 0) | 
	           (ln_ena == 1'b 0) ? 1'b 0 : 1'b 1; 

xgrm_write_cntl U_GRM_WR (

          .reset(reset_wr_clk),
          .clk(wr_clk),
          `ifdef USE_CLK_ENA
          .clk_ena(wr_clk_ena),
          `endif            
          .pcs_wren(lb_wren_c), 
          .ff_afull(ff_afull),
          .ff_wren(ff_wren));

assign ff_din = lb_din; //  FIFO Write Data

//  Rx FIFO
//  -------
// NOTE: Almost full threshold changed to an absolute value!
//a_fifo_pcs_xgxs #(72,16, 4, 3, 1) U_GRM_FF (
a_fifo_pcs_xgxs #(72,16, 4, 4'd12, 4'd1) U_GRM_FF (

          .reset_wclk(reset_wr_clk),
          .reset_rclk(reset_rd_clk),
          .wclk(wr_clk),
          `ifdef USE_CLK_ENA
          .wclk_ena(wr_clk_ena),
          `endif 
          .wren(ff_wren),
          .din(ff_din),
          .rclk(rd_clk),
          `ifdef USE_CLK_ENA
          .rclk_ena(rd_clk_ena),
          `endif
          .rden(ff_rden),
          .dout(ff_dout_int),
          .section(sav_section[3:0]),
          .sect_e(sep_section[3:0]),
          .sav(ff_sav),
          .septy(ff_septy),
          .afull(ff_afull),
          .aempty(ff_aempty));

// determine if idle insertion is allowed after current word
//
always@(ff_dout_int)
begin

        if ((ff_dout_int[71]==1'b1 & ff_dout_int[63:56]!=8'hFE) |
           ((ff_dout_int[64]==1'b1 & ff_dout_int[7:0]==8'h9C)   & (ff_dout_int[68]==1'b1 & ff_dout_int[39:32]==8'h9C)))
        begin
        
                ff_insert = 1'b1 ;
                
        end
        else
        begin
        
                ff_insert = 1'b0 ;
                
        end
        
end



// read STM
// --------
always @(posedge reset_rd_clk or posedge rd_clk)
   begin : r_stmr
        if (reset_rd_clk == 1'b 1)
        begin

                state <= RSTM_TYP_FAULT;	

        end
        else
        begin
                `ifdef USE_CLK_ENA
                if(rd_clk_ena == 1'b 1)
                begin
                `endif            

                state <= nextstate;
                
                `ifdef USE_CLK_ENA
                end
                `endif            

        end
end
       
always @(state or ff_aempty or ff_sav or ff_insert)
begin : c_stmr
        
        case (state)
        
        RSTM_TYP_FAULT:
        begin
                if( ff_aempty==1'b 0 )
                begin
                        nextstate = RSTM_TYP_READ;
                end
                else
                begin
                        nextstate = RSTM_TYP_FAULT;
                end
        end
        
        RSTM_TYP_READ:
        begin
                if( ff_aempty==1'b 1 )
                begin
                        nextstate = RSTM_TYP_FAULT;
                end
                else if( ff_sav==1'b 0 & ff_insert==1'b 1 )
                begin
                        nextstate = RSTM_TYP_INSERT;
                end
                else
                begin
                        nextstate = RSTM_TYP_READ;        
                end
        end

        RSTM_TYP_INSERT:
        begin
                        nextstate = RSTM_TYP_READ;
        end

        default:
        begin
                nextstate = RSTM_TYP_FAULT;
        end

        endcase
end

assign ff_rden = (nextstate==RSTM_TYP_READ) ? 1'b 1 : 1'b 0;


// FIFO output manipulation for idle insertion
// ------------------------------------------- 
always @( state or ff_dout_int )
begin
        case (state)
        
        RSTM_TYP_READ:
        begin
                ff_dout = ff_dout_int;
        end
        
        RSTM_TYP_INSERT:
        begin
                ff_dout = {8'h ff, 64'h 0707070707070707};
        end

        default:
        begin
        
                //  FIFO Empty - Overwrite with Fault Sequence
                //  ------------------------------------------
                
                if (RF_ON_FAULT == 0)
                begin
                        ff_dout = {8'b 00010001, 64'h 0100009C0100009C};	//  Local Fault  (if used in RX)
                end
                else
                begin
                        ff_dout = {8'b 00010001, 64'h 0200009C0200009C};	//  Remote Fault (if used in TX)
                end

        end
        
        endcase
end

//  ---------
always @(posedge reset_rd_clk or posedge rd_clk)
   begin : process_4
   if (reset_rd_clk == 1'b 1)
      begin
      out_dat <= {64{1'b 0}};	
      out_ctl <= {8{1'b 0}};	
      rd_fail <= 1'b 0;	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(rd_clk_ena == 1'b 1)
            begin
         `endif

                out_dat <= ff_dout[63:0];	
                out_ctl <= ff_dout[71:64];	

                if( state==RSTM_TYP_FAULT )
                begin
                        rd_fail <= 1'b 1;
                end
                else
                begin
                        rd_fail <= 1'b 0;
                end
                

         `ifdef USE_CLK_ENA
            end
         `endif
      
      end
   end


endmodule // module grm_fifo